#pragma once

#include <iostream>
#include <string>
#include <cstring>
#include <unordered_map>
#include <cassert>
#include <functional>
#include "Sock.hpp"
#include "Log.hpp"
#include "Epoller.hpp"
#include "Protocol.hpp"
#include "Util.hpp"

using namespace protocol_ns;
const static int gport = 8080;
const static int bsize = 1024;

class Connection;
class EpollServer;

using func_t = function<void(Connection*, const Request&)>;
using callback_t = function<void(Connection*)>;

class Connection{
public:
    Connection(const int& fd, const string& clientIp, const uint16_t& clientPort)
        : fd_(fd), clientIp_(clientIp), clientPort_(clientPort){

    }

    void Register(callback_t recver, callback_t sender, callback_t excepter){
        recver_ = recver;
        sender_ = sender;
        excepter_ = excepter;
    }

    ~Connection(){

    }

    //IO信息
    int fd_;
    string inbuffer_;
    string outbuffer_;

    //IO处理函数
    callback_t recver_;
    callback_t sender_;
    callback_t excepter_;

    //用户信息
    string clientIp_;
    uint16_t clientPort_;

    //关心的事件
    uint32_t events_;

    //回指指针
    EpollServer* es_;
};

class EpollServer{

    static const int gnum = 64;

public:
    EpollServer(func_t func, uint16_t port = gport): func_(func), port_(port){

    }

    void InitServer(){
        listenSock_.Socket();
        listenSock_.Bind(port_);
        listenSock_.Listen();
        epoller_.Create();
        AddConnection(listenSock_.GetSock(), EPOLLIN | EPOLLET);
        logMessage(Debug, "init server success");
    }

    int Dispatcher(){
        int timeout = -1;
        while(true){
            LoopOnce(timeout);
        }
    }

    void LoopOnce(int timeout){
        int n = epoller_.Wait(revs_, gnum, timeout);
        for(int i = 0; i < n; i++){
            int fd = revs_[i].data.fd;
            uint32_t events = revs_[i].events;
            logMessage(Debug, "当前正在处理%d上的%s", fd, (events & EPOLLIN) ? "EPOLLIN" : "OTHER");

            if((events & EPOLLERR) || (events & EPOLLHUP))
                events |= (EPOLLIN | EPOLLOUT);
            if((events & EPOLLIN) && ConnIsExist(fd))
                connections_[fd]->recver_(connections_[fd]);
            if((events & EPOLLOUT) && ConnIsExist(fd))
                connections_[fd]->sender_(connections_[fd]);
        }
    }

    void AddConnection(int fd, uint32_t events, string ip = "127.0.0.1", uint16_t port = gport){
        //设置fd是非阻塞
        if(events & EPOLLET)
            Util::SetNonBlock(fd);
        
        //构建connection对象, 交给connection_来进行管理
        Connection* conn = new Connection(fd, ip, port);
        if(fd == listenSock_.GetSock())
            conn->Register(std::bind(&EpollServer::Accepter, this, placeholders::_1), nullptr, nullptr);
        else{
            conn->Register(std::bind(&EpollServer::Recer, this, std::placeholders::_1),
                           std::bind(&EpollServer::Sender, this, std::placeholders::_1),
                           std::bind(&EpollServer::Excepter, this, std::placeholders::_1));
        }

        conn->events_ = events;
        conn->es_ = this;
        connections_.insert(pair<int, Connection*>(fd, conn));

        //将fd events 写入内核
        bool r = epoller_.AddModEvent(fd, events, EPOLL_CTL_ADD);
        assert(r);
        (void)r;

        logMessage(Debug, "AddConnection success, fd: %d, clientinfo: [%s:%d]", fd, ip.c_str(), port);
    }

    void Accepter(Connection* conn){
        do{
            string clientIp;
            uint16_t clientPort;
            int err = 0;
            int sock = listenSock_.Accept(&clientIp, &clientPort, &err);
            if(sock > 0){
                logMessage(Debug, "%s:%d 已经连上了服务器了", clientIp.c_str(), clientPort);
                AddConnection(sock, EPOLLIN | EPOLLET, clientIp, clientPort);
            }
            else{
                if(err == EAGAIN || err == EWOULDBLOCK)
                    break;
                else if(err == EINTR)
                    continue;
                else{
                    logMessage(Warning, "errstring : %s, errcode: %d", strerror(err), err);
                    continue;
                }
            }
        }while(conn->events_ & EPOLLET);

        logMessage(Debug, "accepter done");
    }

    void Recer(Connection* conn){
        do{
            char buffer[1024];
            ssize_t n = recv(conn->fd_, buffer, sizeof(buffer)-1, 0);
            if(n > 0){
                buffer[n] = 0;
                conn->inbuffer_ += buffer;
                string requestStr;
                int n = protocol_ns::ReadPackage(conn->inbuffer_, &requestStr);
                if(n > 0){
                    requestStr = protocol_ns::RemoveHeader(requestStr, n);
                    Request req;
                    req.Deserialize(requestStr);
                    func_(conn, req);
                }
            }
            else if(n == 0){
                conn->excepter_(conn);
                break;
            }
            else{
                if(errno == EAGAIN || errno == EWOULDBLOCK)
                    break;
                else if(errno == EINTR)
                    continue;
                else{
                    conn->excepter_(conn);
                    break;
                }
            }
        }while(conn->events_ & EPOLLET);
    }

    void Sender(Connection* conn){
        do{
            ssize_t n = send(conn->fd_, conn->outbuffer_.c_str(), conn->outbuffer_.size(), 0);
            if(n > 0){
                conn->outbuffer_.erase(0,n);
                if(conn->outbuffer_.empty()){
                    EnableReadWrite(conn, true, false);
                    break;
                }
                else
                    EnableReadWrite(conn, true, true);
            }
            else{
                if(errno == EAGAIN || errno == EWOULDBLOCK)
                    break;
                else if(errno == EINTR)
                    continue;
                else
                {
                    conn->excepter_(conn);
                    break;
                }
            }
        }while(conn->events_ & EPOLLET);
    }

    void Excepter(Connection* conn){
        logMessage(Debug, "Excepter..., fd: %d, clientinfo: [%s:%d]", conn->fd_, conn->clientIp_.c_str(), conn->clientPort_);
    }

    bool EnableReadWrite(Connection* conn, bool readable, bool writable){
        conn->events_ = ((readable ? EPOLLIN : 0) | (writable ? EPOLLOUT : 0) | EPOLLET);
        return epoller_.AddModEvent(conn->fd_, conn->events_, EPOLL_CTL_MOD);
    }

    bool ConnIsExist(int fd){
        return connections_.find(fd) != connections_.end();
    }

    ~EpollServer(){
        listenSock_.Close();
        epoller_.Close();
    }

private:
    uint16_t port_;
    Sock listenSock_;
    Epoller epoller_;
    struct epoll_event revs_[gnum];
    func_t func_;
    unordered_map<int, Connection*> connections_;
};